﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

/**
 * Pasta Componentes
 * Extensões:
 *   rgx: RegExp
 *   lua: Scripts Lua
 *   
 * Nomenclatura dos arquivos:
 * ComponentType
 *  - Replace    : rpl_name.*
 *  - Find Query : fin_name.*
 */

namespace AzaEngine
{
    public enum ComponentType
    {
        None = 0,
        Replace,
        Query,
        FindFile
    }

    public enum ComponentParameterType
    {
        Null = 0,
        String,
        Integer,
        Bool,
        List
    }

    public class ComponentParameter
    {
        public string Label { get; set; }
        private string defaultValue;
        public ComponentParameterType Type { get; set; }
        public string List { get; set; }

        public ComponentParameter()
        {
            Label = string.Empty;
            this.Type = ComponentParameterType.Null;
        }

        public void Set(ComponentParameterType type, string label)
        {
            this.Type = type;
            Label = label;
        }

        public void Set(string type, string label)
        {
            SetType(type);
            Label = label;
        }

        public void SetDefaultValue(string _value)
        {
            defaultValue = _value;
        }

        public void SetDefaultValue(bool _value)
        {
            defaultValue = Convert.ToString(_value);
        }

        public void SetDefaultValue(int _value)
        {
            defaultValue = _value.ToString();
        }

        public string GetDefaultValue()
        {
            return defaultValue;
        }

        public void SetType(string type)
        {
            switch (type.ToLower())
            {
                case "string":
                    {
                        Type = ComponentParameterType.String;
                        break;
                    }
                case "integer":
                    {
                        Type = ComponentParameterType.Integer;
                        break;
                    }
                case "bool":
                    {
                        Type = ComponentParameterType.Bool;
                        break;
                    }
                case "list":
                    {
                        Type = ComponentParameterType.List;
                        break;
                    }
                default:
                    {
                        Type = ComponentParameterType.Null;
                        break;
                    }
            }
        }
    }



    public class Component
    {
        public ComponentParameter[] Parameters = new ComponentParameter[6];
        public ScriptType ScriptType { get; set; }
        public ComponentType Type { get; set; }
        public string ID { get; set; }
        public string Description { get; set; }
        public string LongDescription { get; set; }

        public static List<string> ComponentPrefix = new List<string> { "rpl", "fin" };

        public static string ComponentTypePrefix(ComponentType type) { return ComponentPrefix[(int)type]; }

        // Busca o tipo do componente (ComponentType) com base na id de componente
        public static ComponentType GetComponentTypeById(string id, List<Component> comp)
        {
            foreach (Component c in comp) { if (c.ID == id) return c.Type; }
            return ComponentType.None;
        }
        // Busca o tipo do componente (ComponentType) com base na id de componente e com a Coleção de Componentes default da classe Engine
        public static ComponentType GetComponentTypeById(string id)
        {
            return GetComponentTypeById(id, Engine.Components);
        }


        public Component()
        {
            for (int i = 0; i < Parameters.Length; i++)
            {
                Parameters[i] = new ComponentParameter();
            }

            Parameters[0].Type = ComponentParameterType.Null;
            Parameters[0].Label = "Reserved";
        }

        public Component(Enum id, ComponentType type, string description)
            : this()
        {
            ID = id.ToString();
            Description = description;
            ScriptType = ScriptType.Internal;
        }

        public Component(string id, ComponentType type, ScriptType scriptType, string description)
            : this()
        {
            ID = id;
            Type = type;
            ScriptType = scriptType;
            Description = description;
        }

        public void SetParameter(int id, ComponentParameterType type, string label)
        {
            Parameters[id].Set(type, label);
        }
    }

    public class ComponentSuite : List<Component>
    {
        // Adiciona um componente
        public void Add(string id, ComponentType type, ScriptType scriptType, string description)
        {
            this.Add(new Component(id, type, scriptType, description));
        }

        // Adicionar um componente Interno
        public void AddInternal(Enum id, ComponentType type, string description)
        {
            Add(id.ToString(), type, ScriptType.Internal, description);
        }
    }

    public static class ComponentController
    {
        // Carrega todos os componentes
        public static List<Component> Load(string dir)
        {
            List<Component> ret = new List<Component>();

            // Adiciona Componentes de Replace
            ret.InsertRange(ret.Count, Load(dir, ComponentType.Replace));

            return ret;
        }

        // Carrega apenas os componentes do tipo solicitado
        public static List<Component> Load(string dir, ComponentType Type)
        {
            bool skipFile = false;
            List<Component> ret = new List<Component>();
            string prefixo = Component.ComponentTypePrefix(Type);

            if (Directory.Exists(dir))
            {
                foreach (string f in Directory.GetFiles(dir, prefixo + "_*.*"))
                {
                    skipFile = false;

                    Component component = new Component();

                    // Id
                    component.ID = Path.GetFileNameWithoutExtension(f);

                    // Type
                    component.Type = Type;

                    // Script Type
                    switch (Path.GetExtension(f).ToLower())
                    {
                        case "rgx":
                            {
                                component.ScriptType = ScriptType.Regexp;
                                break;
                            }

                        case "lua":
                            {
                                component.ScriptType = ScriptType.Lua;
                                break;
                            }

                        default:
                            {
                                skipFile = true;
                                break;
                            }
                    }

                    // Descrição e parâmetros
                    StreamReader reader = new StreamReader(f);
                    string line = string.Empty;
                    while ((line = reader.ReadLine()) != null)
                    {
                        skipFile = LineToSetComponent(line, ref component);
                    }
                    reader.Close();

                    if (!skipFile)
                    {
                        ret.Add(component);
                    }

                    Console.WriteLine(f);
                }
            }

            return ret;
        }

        public static bool LineToSetComponent(string line, ref Component component)
        {
            string par = string.Empty;
            string label = string.Empty;
            string partype = string.Empty;
            int parId = 0;
            bool Success = true;

            try
            {
                // Description
                //if (line.IndexOf('//') > -1 || line.IndexOf('--') > -1)
                {
                    if (line.IndexOf("@description") > -1)
                    {
                        component.Description = line.Substring(line.LastIndexOf("@description") + "@description".Length + 1);
                    }

                    // Parâmetros
                    if (line.IndexOf("@par") > -1)
                    {
                        par = line.Substring(line.IndexOf("@par") + "@par".Length);
                        parId = Convert.ToInt32(par.Substring(0, 1));

                        if (parId >= 1 && parId <= component.Parameters.Length)
                        {
                            if (par.Length > 2)
                            {
                                // Type
                                par = par.Substring(2).Trim();
                                if (par.IndexOf(' ') > -1)
                                {
                                    partype = par.Substring(0, par.IndexOf(' '));
                                }
                                else
                                {
                                    partype = par;
                                }

                                partype = partype.ToLower();

                                // Pega lista de valores, quando for Lista
                                if (partype.Substring(0, 4).ToLower() == "list")
                                {
                                    partype = "list";
                                    if (par.IndexOf(')') > -1)
                                    {
                                        par = par.Substring(5).Trim();
                                        if (par.IndexOf(' ') > -1)
                                        {
                                            component.Parameters[parId].List = par.Substring(0, par.IndexOf(')'));
                                            par = par.Substring(component.Parameters[parId].List.Length + 1);
                                        }
                                        else { Success = false; }
                                    }
                                    else { Success = false; }
                                }
                                else
                                {
                                    par = par.Substring(partype.Length);
                                }

                                // Label
                                label = par.Trim();
                            }

                            component.Parameters[parId].SetType(partype);
                            component.Parameters[parId].Label = label;
                        }
                        else
                        {
                            Success = false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            return Success;
        }
    }
}
